/* eslint-disable no-useless-escape */
/* eslint-disable eol-last */
/* eslint-disable no-case-declarations */
/* eslint-disable standard/no-callback-literal */
/* eslint-disable eqeqeq */
/* eslint-disable indent */
/**
 * 存储localStorage
 */
export const setStore = (name, content) => {
    if (!name) return
    if (typeof content !== 'string') {
        content = JSON.stringify(content)
    }
    window.localStorage.setItem(name, content)
}

/**
 * 获取localStorage
 */
export const getStore = name => {
    if (!name) return
    return window.localStorage.getItem(name)
}

/**
 * 删除localStorage
 */
export const removeStore = name => {
    if (!name) return
    window.localStorage.removeItem(name)
}

/**
 * 获取style样式
 */
export const getStyle = (element, attr, NumberMode = 'int') => {
    let target
        // scrollTop 获取方式不同，没有它不属于style，而且只有document.body才能用
    if (attr === 'scrollTop') {
        target = element.scrollTop
    } else if (element.currentStyle) {
        target = element.currentStyle[attr]
    } else {
        target = document.defaultView.getComputedStyle(element, null)[attr]
    }
    // 在获取 opactiy 时需要获取小数 parseFloat
    return NumberMode == 'float' ? parseFloat(target) : parseInt(target)
}

/**
 * 页面到达底部，加载更多
 */
export const loadMore = (element, callback) => {
    const windowHeight = window.screen.height
    let height
    let setTop
    let paddingBottom
    let marginBottom
    let requestFram
    let oldScrollTop

    document.body.addEventListener('scroll', () => {
            loadMore()
        }, false)
        // 运动开始时获取元素 高度 和 offseTop, pading, margin
    element.addEventListener('touchstart', () => {
        height = element.offsetHeight
        setTop = element.offsetTop
        paddingBottom = getStyle(element, 'paddingBottom')
        marginBottom = getStyle(element, 'marginBottom')
    }, { passive: true })

    // 运动过程中保持监听 scrollTop 的值判断是否到达底部
    element.addEventListener('touchmove', () => {
        loadMore()
    }, { passive: true })

    // 运动结束时判断是否有惯性运动，惯性运动结束判断是非到达底部
    element.addEventListener('touchend', () => {
        oldScrollTop = document.body.scrollTop
        moveEnd()
    }, { passive: true })

    const moveEnd = () => {
        requestFram = requestAnimationFrame(() => {
            if (document.body.scrollTop != oldScrollTop) {
                oldScrollTop = document.body.scrollTop
                loadMore()
                moveEnd()
            } else {
                cancelAnimationFrame(requestFram)
                    // 为了防止鼠标抬起时已经渲染好数据从而导致重获取数据，应该重新获取dom高度
                height = element.offsetHeight
                loadMore()
            }
        })
    }

    const loadMore = () => {
        if (document.body.scrollTop + windowHeight >= height + setTop + paddingBottom + marginBottom) {
            callback()
        }
    }
}

/**
 * 显示返回顶部按钮，开始、结束、运动 三个过程中调用函数判断是否达到目标点
 */
export const showBack = callback => {
    let requestFram
    let oldScrollTop

    document.addEventListener('scroll', () => {
        showBackFun()
    }, false)
    document.addEventListener('touchstart', () => {
        showBackFun()
    }, { passive: true })

    document.addEventListener('touchmove', () => {
        showBackFun()
    }, { passive: true })

    document.addEventListener('touchend', () => {
        oldScrollTop = document.body.scrollTop
        moveEnd()
    }, { passive: true })

    const moveEnd = () => {
        requestFram = requestAnimationFrame(() => {
            if (document.body.scrollTop != oldScrollTop) {
                oldScrollTop = document.body.scrollTop
                moveEnd()
            } else {
                cancelAnimationFrame(requestFram)
            }
            showBackFun()
        })
    }

    // 判断是否达到目标点
    const showBackFun = () => {
        if (document.body.scrollTop > 500) {
            callback(true)
        } else {
            callback(false)
        }
    }
}

/**
 * 运动效果
 * @param {HTMLElement} element   运动对象，必选
 * @param {JSON}        target    属性：目标值，必选
 * @param {number}      duration  运动时间，可选
 * @param {string}      mode      运动模式，可选
 * @param {function}    callback  可选，回调函数，链式动画
 */
export const animate = (element, target, duration = 400, mode = 'ease-out', callback) => {
    clearInterval(element.timer)

    // 判断不同参数的情况
    if (duration instanceof Function) {
        callback = duration
        duration = 400
    } else if (duration instanceof String) {
        mode = duration
        duration = 400
    }

    // 判断不同参数的情况
    if (mode instanceof Function) {
        callback = mode
        mode = 'ease-out'
    }

    // 获取dom样式
    const attrStyle = attr => {
            if (attr === 'opacity') {
                return Math.round(getStyle(element, attr, 'float') * 100)
            } else {
                return getStyle(element, attr)
            }
        }
        // 根字体大小，需要从此将 rem 改成 px 进行运算
    const rootSize = parseFloat(document.documentElement.style.fontSize)

    const unit = {}
    const initState = {}

    // 获取目标属性单位和初始样式值
    Object.keys(target).forEach(attr => {
        if (/[^\d^\.]+/gi.test(target[attr])) {
            unit[attr] = target[attr].match(/[^\d^\.]+/gi)[0] || 'px'
        } else {
            unit[attr] = 'px'
        }
        initState[attr] = attrStyle(attr)
    })

    // 去掉传入的后缀单位
    Object.keys(target).forEach(attr => {
        if (unit[attr] == 'rem') {
            target[attr] = Math.ceil(parseInt(target[attr]) * rootSize)
        } else {
            target[attr] = parseInt(target[attr])
        }
    })

    let flag = true // 假设所有运动到达终点
    const remberSpeed = {} // 记录上一个速度值,在ease-in模式下需要用到
    element.timer = setInterval(() => {
        Object.keys(target).forEach(attr => {
            let iSpeed = 0 // 步长
            let status = false // 是否仍需运动
            const iCurrent = attrStyle(attr) || 0 // 当前元素属性址
            let speedBase = 0 // 目标点需要减去的基础值，三种运动状态的值都不同
            let intervalTime // 将目标值分为多少步执行，数值越大，步长越小，运动时间越长
            switch (mode) {
                case 'ease-out':
                    speedBase = iCurrent
                    intervalTime = duration * 5 / 400
                    break
                case 'linear':
                    speedBase = initState[attr]
                    intervalTime = duration * 20 / 400
                    break
                case 'ease-in':
                    const oldspeed = remberSpeed[attr] || 0
                    iSpeed = oldspeed + (target[attr] - initState[attr]) / duration
                    remberSpeed[attr] = iSpeed
                    break
                default:
                    speedBase = iCurrent
                    intervalTime = duration * 5 / 400
            }
            if (mode !== 'ease-in') {
                iSpeed = (target[attr] - speedBase) / intervalTime
                iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed)
            }
            // 判断是否达步长之内的误差距离，如果到达说明到达目标点
            switch (mode) {
                case 'ease-out':
                    status = iCurrent != target[attr]
                    break
                case 'linear':
                    status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed)
                    break
                case 'ease-in':
                    status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed)
                    break
                default:
                    status = iCurrent != target[attr]
            }

            if (status) {
                flag = false
                    // opacity 和 scrollTop 需要特殊处理
                if (attr === 'opacity') {
                    element.style.filter = 'alpha(opacity:' + (iCurrent + iSpeed) + ')'
                    element.style.opacity = (iCurrent + iSpeed) / 100
                } else if (attr === 'scrollTop') {
                    element.scrollTop = iCurrent + iSpeed
                } else {
                    element.style[attr] = iCurrent + iSpeed + 'px'
                }
            } else {
                flag = true
            }

            if (flag) {
                clearInterval(element.timer)
                if (callback) {
                    callback()
                }
            }
        })
    }, 20)
}